﻿using System;
using System.Collections.Generic;
using System.Text;
using OpenGameEngine.GameObjects;
using OpenGameEngine;
using OpenGameEngine.Base;
using OpenGameEngine.Audio;
using System.Globalization;
using System.IO;

namespace ChopperGame
{
    public class Level : GameObject
    {
        private RectangleF[] Walls;
        private RectangleF[] Obstacles;
        private Cloud[] Clouds;

        private Chopper Player;
        private float Gap;
        private float LevelSpeed;
        private float GapClosingSpeed;
        private Random Randomizer;
        private float Rise;
        private float MaxWallBound;
        private bool Up;
        private Int64 Distance;
        public Int64 BestDistance { get; private set; }
        public bool SoundsOn;
        public bool ObstaclesOn;
        private double WaitingTime;
        private double ElapsedTime;

        private AudioPlayer ExplosionSound;

        private SolidColorBrush WallBrush;
        private RectangleF ScreenBounds;
        private SolidColorBrush BackgroundBrush;
        private SolidColorBrush TextBrush;
        private Font TextFont;
        private BinaryReader SaveReader;
        private BinaryWriter SaveWriter;
        private String SaveFilePath;

        public Level()
            : base()
        {
            this.AcceptsKeyboardInput = true;
            this.Enabled = true;
        }

        public override void Load(OpenGameEngine.Base.GameCoreEventArgs e)
        {
            base.Load(e);

            Walls = new RectangleF[15];
            Obstacles = new RectangleF[2];
            Clouds = new Cloud[8];

            Player = new Chopper();

            ExplosionSound = AudioEngine.CreatePlayer("Assets\\explosion.wav");
            ExplosionSound.SetVolume(0.3f);

            GapClosingSpeed = 0.03f;
            Randomizer = new Random();
            Rise = 25f;
            Up = true;
            WaitingTime = 2;
            ObstaclesOn = true;
            SoundsOn = true;
            WallBrush = new SolidColorBrush(Color.Black);
            ScreenBounds = new RectangleF(0, 0, e.Core.Config.Width, e.Core.Config.Height);
            BackgroundBrush = new SolidColorBrush(Color.CornflowerBlue);
            TextBrush = new SolidColorBrush(Color.White);
            TextFont = new Font("Comic Sans MS", 17f);

            String appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
            SaveFilePath = appData + "\\MatthiWare\\ChopperGame\\score.bin";

            FileInfo fi = new FileInfo(SaveFilePath);
            if (!fi.Directory.Exists)
                fi.Directory.Create();


            using (BinaryReader SaveReader = new BinaryReader(File.Open(SaveFilePath, FileMode.OpenOrCreate, FileAccess.Read)))
                if (SaveReader.BaseStream.Length > 0)
                    this.BestDistance = SaveReader.ReadInt64();

            StartNewLevel(e);

            this.Visible = true;
        }

        public void CrashWait(GameCoreEventArgs e)
        {
            this.ElapsedTime += e.Time.FrameTime;
            if (ElapsedTime >= this.WaitingTime)
            {
                this.ElapsedTime = 0.0;

                StartNewLevel(e);

                using (BinaryWriter SaveWriter = new BinaryWriter(File.Open(SaveFilePath, FileMode.OpenOrCreate, FileAccess.Write)))
                    SaveWriter.Write(this.BestDistance);

                e.Core.SwitchScene("MenuScene");
            }
        }

        public void Crash()
        {
            this.Player.Crashed = true;

            if (SoundsOn && AudioEngine.Running)
            {
                ExplosionSound.Stop();
                ExplosionSound.Start();
            }
        }

        private void Randomize()
        {
            this.MaxWallBound = Randomizer.Next(20, (int)(600 - this.Gap));
            this.Rise = Randomizer.Next(3, 40);
        }

        private void StartNewLevel(GameCoreEventArgs e)
        {
            this.Gap = 400f;
            this.LevelSpeed = 6f;

            Player = new Chopper();

            for (int wallId = 0; wallId < Walls.Length; wallId++)
            {
                this.Walls[wallId] = new RectangleF(wallId * 60, (e.Core.Config.Height - this.Gap) / 2f, 60, this.Gap);
            }

            for (int obsId = 0; obsId < Obstacles.Length; obsId++)
            {
                this.Obstacles[obsId] = new RectangleF((obsId * 600) + 1000, 250, 60, 100);
            }

            for (int cldId = 0; cldId < Clouds.Length; cldId++)
            {
                this.Clouds[cldId] = new Cloud(120, 257);
                this.Clouds[cldId].Load(e);
            }

            this.Clouds[0].Started = true;
            this.Clouds[0].Visible = true;

            Up = (Randomizer.Next(2) == 0) ? true : false;

            Randomize();

            if (Distance > BestDistance)
                BestDistance = Distance;
            Distance = 0;
        }

        private void MoveWalls(GameCoreEventArgs e)
        {
            for (int wallId = 0; wallId < Walls.Length; wallId++)
            {
                Walls[wallId].X -= this.LevelSpeed;
            }

            for (int obsId = 0; obsId < Obstacles.Length; obsId++)
            {
                this.Obstacles[obsId].X -= this.LevelSpeed;
            }

            for (int cldId = 0; cldId < Clouds.Length; cldId++)
            {
                Cloud cld = this.Clouds[cldId];
                if (cld.Started)
                    cld.X -= this.LevelSpeed;
            }

            if (this.Gap >= 300)
                this.Gap -= this.GapClosingSpeed;

            this.Distance++;
        }

        private void UpdateWalls(GameCoreEventArgs e)
        {
            if (Walls[0].X < -60)
            {
                for (int wallId = 0; wallId < (Walls.Length - 1); wallId++)
                {
                    this.Walls[wallId] = this.Walls[wallId + 1];
                }

                if (Up)
                {
                    this.Walls[this.Walls.Length - 1] = new RectangleF(this.Walls[this.Walls.Length - 2].X + 60, this.Walls[this.Walls.Length - 2].Y - this.Rise, 60, this.Gap);

                    if (this.Walls[this.Walls.Length - 1].Y < (this.MaxWallBound))
                    {
                        Up = false;
                        Randomize();
                    }

                }
                else
                {
                    this.Walls[this.Walls.Length - 1] = new RectangleF(this.Walls[this.Walls.Length - 2].X + 60, this.Walls[this.Walls.Length - 2].Y + this.Rise, 60, this.Gap);

                    if (this.Walls[this.Walls.Length - 1].Bottom > (600 - this.MaxWallBound))
                    {
                        Up = true;
                        Randomize();
                    }
                }
            }

            if (this.Obstacles[0].X < -400)
            {
                for (int obsId = 0; obsId < (Obstacles.Length - 1); obsId++)
                {
                    this.Obstacles[obsId] = this.Obstacles[obsId + 1];
                }

                this.Obstacles[this.Obstacles.Length - 1] = new RectangleF(this.Obstacles[this.Obstacles.Length - 2].X + 600, this.Randomizer.Next((Int32)(this.Walls[this.Walls.Length - 1].Y), (Int32)(this.Walls[this.Walls.Length - 1].Bottom - 100)), 60, 100);
            }
        }

        private void UpdateClouds(GameCoreEventArgs e)
        {
            for (int i = 0; i < (Clouds.Length - 1); i++)
            {
                if (!Clouds[i].IsLoaded)
                    Clouds[i].Load(e);

                if (!Clouds[i + 1].IsLoaded)
                    Clouds[i + 1].Load(e);

                if ((Clouds[i].Started && !Clouds[i + 1].Started) && (Clouds[i].X + 20 < Clouds[i + 1].X))
                {
                    Clouds[i + 1] = new Cloud(this.Player.X, this.Player.Y);
                    Clouds[i + 1].Load(e);
                    Clouds[i + 1].Started = true;

                    if (this.Player.Power)
                        Clouds[i + 1].Visible = true;
                    else
                        Clouds[i + 1].Visible = false;
                }
            }

            if (this.Clouds[0].Position.X < -20)
            {
                for (int cldId = 0; cldId < (Clouds.Length - 1); cldId++)
                {
                    this.Clouds[cldId] = this.Clouds[cldId + 1];

                    if (!this.Clouds[cldId].IsLoaded)
                        this.Clouds[cldId].Load(e);
                }

                this.Clouds[this.Clouds.Length - 1] = new Cloud(this.Player.Position.X + (this.Player.Size.Width / 2), this.Player.Position.Y + (this.Player.Size.Height / 2));
                this.Clouds[this.Clouds.Length - 1].Load(e);
                this.Clouds[this.Clouds.Length - 1].Started = true;

                if (Player.Power)
                    this.Clouds[this.Clouds.Length - 1].Visible = true;
                else
                    this.Clouds[this.Clouds.Length - 1].Visible = false;

            }
        }

        public override void Update(OpenGameEngine.Base.GameCoreEventArgs e)
        {
            base.Update(e);

            if (!this.Player.Crashed)
            {
                UpdateWalls(e);
                MoveWalls(e);
                UpdateClouds(e);

                WallCollision(e);
            }

            if (Player.Enabled)
            {
                if (!Player.IsLoaded)
                    Player.Load(e);

                if (Player.IsLoaded)
                    Player.Update(e);
            }

            if (this.Player.Crashed)
                CrashWait(e);

            if ((this.Distance % 1000) == 0)
                this.LevelSpeed += 1f;
        }

        private void WallCollision(GameCoreEventArgs e)
        {
            for (int wallId = 0; wallId < (Walls.Length - 1); wallId++)
            {
                RectangleF upRect = new RectangleF(this.Walls[wallId].X, 0, 60, this.Walls[wallId].Y);
                RectangleF downRect = new RectangleF(this.Walls[wallId].X, this.Walls[wallId].Bottom, 60, e.Core.Config.Height - this.Walls[wallId].Bottom);

                if (((RectangleF)Player).Intersects(upRect) || ((RectangleF)Player).Intersects(downRect))
                {
                    this.Crash();
                    return;
                }
            }

            if (this.ObstaclesOn)
            {
                for (int obsId = 0; obsId < (this.Obstacles.Length - 1); obsId++)
                {
                    if (((RectangleF)this.Player).Intersects(this.Obstacles[obsId]))
                    {
                        this.Crash();
                        return;
                    }
                }
            }
        }

        public override void Draw(OpenGameEngine.Base.DrawEventArgs e)
        {
            e.FillRectangle(ScreenBounds, BackgroundBrush);

            base.Draw(e);

            foreach (RectangleF wall in Walls)
                e.FillRectangle(wall, WallBrush);

            if (this.ObstaclesOn)
            {
                foreach (RectangleF obs in this.Obstacles)
                    e.FillRectangle(obs, BackgroundBrush);
            }

            foreach (Cloud cloud in Clouds)
            {
                if (cloud.IsLoaded && cloud.Visible)
                    cloud.Draw(e);
            }

            if (Player.IsLoaded && Player.Visible)
                Player.Draw(e);

            e.DrawString("Distance: " + Distance.ToString(CultureInfo.CurrentCulture), TextFont, TextBrush, 10, 570);
            String bestDistance = "Best: " + this.BestDistance.ToString(CultureInfo.CurrentCulture);
            e.DrawString(bestDistance, TextFont, TextBrush, (800 - Font.GetTextSize(bestDistance, TextFont).Width) - 10, 570);
        }

        public override void KeyPress(GameCoreEventArgs e, System.Windows.Forms.Keys key)
        {
            base.KeyPress(e, key);

            if (key == System.Windows.Forms.Keys.Escape)
                e.Core.SwitchScene("MenuScene");
        }

        public override void MouseDown(GameCoreEventArgs e, System.Windows.Forms.MouseButtons button)
        {
            Player.MouseDown(e, button);
        }

        public override void MouseUp(GameCoreEventArgs e, System.Windows.Forms.MouseButtons button)
        {
            Player.MouseUp(e, button);
        }
    }
}
